/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.webapp.util.core;

import com.blandware.atleap.common.Constants;
import com.blandware.atleap.model.core.GlobalProperty;
import com.blandware.atleap.service.core.GlobalPropertyManager;
import com.blandware.atleap.service.exception.BeanNotFoundException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.ServletContext;
import java.util.Properties;

/**
 * <p>Wrapper class for GlobalPropertyManager. Holds and synchronizes dynamic and static properties</p>
 * <p><a href="GlobalProperties.java.html"><i>View Source</i></a></p>
 *
 * @author Sergey Zubtsovskiy <a href="mailto:sergey.zubtsovskiy@blandware.com">&lt;sergey.zubtsovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.13 $ $Date: 2007/12/20 13:41:53 $
 */
public final class GlobalProperties {

	/**
	 * Servlet context key under which instance of this class is saved
	 */
	private static final String INSTANCE_KEY = "com.blandware.atleap.webapp.util.GlobalProperties.INSTANCE";

	/**
	 * Commons Logging instance for this class
	 */
	private transient final Log log = LogFactory.getLog(GlobalProperties.class);

	/**
	 * Static properties
	 */
	private Properties staticProperties;

	/**
	 * Dynamic properties
	 */
	private Properties dynamicProperties;

	/**
	 * Global property manager we're using
	 */
	private GlobalPropertyManager globalPropertyManager = null;

	/**
	 * Creates new instance of GlobalProperties and initializes internal fields
	 *
	 * @param servletContext Servlet context
	 */
	private GlobalProperties(ServletContext servletContext) {
		ApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
		globalPropertyManager = (GlobalPropertyManager) applicationContext.getBean(Constants.GLOBAL_PROPERTY_MANAGER_BEAN);
        synchronizeCache();
    }

	/**
	 * Returns instance of GlobalProperties
	 *
     * @param servletContext Servlet context to search by
	 * @return Instance of GlobalProperties
	 */
	public static GlobalProperties getInstance(ServletContext servletContext) {
		GlobalProperties globalProperties = (GlobalProperties) servletContext.getAttribute(INSTANCE_KEY);
		if ( globalProperties == null ) {
			globalProperties = new GlobalProperties(servletContext);
			servletContext.setAttribute(INSTANCE_KEY, globalProperties);
		}
		return globalProperties;
	}

    /**
     * Load data from database into local cache
     */
    public void synchronizeCache() {
        staticProperties = globalPropertyManager.getStaticProperties();
		dynamicProperties = globalPropertyManager.loadDynamicProperties(false);

        if ( log.isInfoEnabled() ) {
            log.info("Global properties synchronized");
        }
    }

    /**
	 * Retrieves global property with specified name as string.<br />
	 * This method first searches in dynamic properties and then in static.
     * So, if you have static and dynamic property with the same name, note that
     * static one will never be returned.
	 *
	 * @param propertyName Name to search by
	 * @return Value or <code>null</code> if no property with specified name exists
	 */
	public String getString(String propertyName) {
		return getString(propertyName, null);
	}


	/**
	 * Retrieves global property with specified name as string. If nothing
     * found, default value is used.<br />
	 * This method first searches in dynamic properties and then in static.
     * So, if you have static and dynamic property with the same name, note that
     * static one will never be returned.
	 *
	 * @param propertyName Name to search by
     * @param defaultValue The value that will be returned if nothing is found
	 * @return Value or default if no property with specified name exists
	 */
	public String getString(String propertyName, String defaultValue) {
		String value = dynamicProperties.getProperty(propertyName);
		if ( value == null ) {
			value = staticProperties.getProperty(propertyName);
		}
		return value != null ? value : defaultValue;
	}

	/**
	 * Retrieves global property with specified name as integer.<br />
     * This method first searches in dynamic properties and then in static.
     * So, if you have static and dynamic property with the same name, note that
     * static one will never be returned.<br />
	 * If an error occur when trying to convert property to integer,
     * <code>null</code> is returned.
	 *
	 * @param propertyName Name to search by
	 * @return Value or null if no property with specified name exists or
     * if property value cannot be converted to integer
	 */
	public Integer getInteger(String propertyName) {
		return getInteger(propertyName, null);
	}

    /**
     * Retrieves global property with specified name as integer. If nothing
     * found, default value is used.<br />
     * This method first searches in dynamic properties and then in static.
     * So, if you have static and dynamic property with the same name, note that
     * static one will never be returned.<br />
     * If an error occur when trying to convert property to integer,
     * <code>null</code> is returned.
     *
     * @param propertyName Name to search by
     * @param defaultValue The value that will be returned if nothing is found
     * @return Value or default if no property with specified name exists or
     * if property value cannot be converted to integer
     */
	public Integer getInteger(String propertyName, int defaultValue) {
		return getInteger(propertyName, new Integer(defaultValue));
	}

	/**
	 * Retrieves global property with specified name as integer. If nothing
     * found, default value is used.<br />
	 * This method first searches in dynamic properties and then in static.
     * So, if you have static and dynamic property with the same name, note that
     * static one will never be returned.<br />
	 * If an error occur when trying to convert property to integer,
     * <code>null</code> is returned.
	 *
	 * @param propertyName Name to search by
     * @param defaultValue The value that will be returned if nothing is found
	 * @return Value or default if no property with specified name exists or
     * if property value cannot be converted to integer
	 */
	public Integer getInteger(String propertyName, Integer defaultValue) {
		String value;
		if ( defaultValue != null ) {
			value = getString(propertyName, defaultValue.toString());
		} else {
			value = getString(propertyName, null);
		}

		Integer result = null;
		if ( value != null ) {
			try {
				result = Integer.valueOf(value);
			} catch ( Exception e ) {
				if ( log.isErrorEnabled() ) {
					log.error("Error while retrieving integer value of property '" + propertyName
					        + "'. Failed to convert string '" + value
					        + "' to java.lang.Integer. Cause: "
					        + e.getClass().getName() + ": " + e.getMessage());
				}
				result = defaultValue;
			}
		}
		return result;
	}

	/**
	 * Retrieves global property with specified name as boolean.<br />
     * This method first searches in dynamic properties and then in static.
     * So, if you have static and dynamic property with the same name, note that
     * static one will never be returned.<br />
	 * If an error occur when trying to convert property to boolean,
     * <code>null</code> is returned.
	 *
	 * @param propertyName Name to search by
	 * @return Value or null if no property with specified name exists or
     * if property value cannot be converted to boolean
	 */
	public Boolean getBoolean(String propertyName) {
		return getBoolean(propertyName, null);
	}

    /**
     * Retrieves global property with specified name as boolean. If nothing
     * found, default value is used.<br />
     * This method first searches in dynamic properties and then in static.
     * So, if you have static and dynamic property with the same name, note that
     * static one will never be returned.<br />
     * If an error occur when trying to convert property to boolean,
     * <code>null</code> is returned.
     *
     * @param propertyName Name to search by
     * @param defaultValue The value that will be returned if nothing is found
     * @return Value or default if no property with specified name exists or
     * if property value cannot be converted to boolean
     */
	public Boolean getBoolean(String propertyName, boolean defaultValue) {
		return getBoolean(propertyName, (defaultValue) ? Boolean.TRUE : Boolean.FALSE);
	}

	/**
	 * Retrieves global property with specified name as boolean. If nothing
     * found, default value is used.<br />
	 * This method first searches in dynamic properties and then in static.
     * So, if you have static and dynamic property with the same name, note that
     * static one will never be returned.<br />
	 * If an error occur when trying to convert property to boolean,
     * <code>null</code> is returned.
	 *
	 * @param propertyName Name to search by
     * @param defaultValue The value that will be returned if nothing is found
	 * @return Value or default if no property with specified name exists or
     * if property value cannot be converted to boolean
	 */
	public Boolean getBoolean(String propertyName, Boolean defaultValue) {
		String value;
		if ( defaultValue != null ) {
			value = getString(propertyName, defaultValue.toString());
		} else {
			value = getString(propertyName, null);
		}

		Boolean result = null;
		if ( value != null ) {
			try {
				result = Boolean.valueOf(value);
			} catch ( Exception e ) {
				if ( log.isErrorEnabled() ) {
					log.error("Error while retrieving boolean value of property '" + propertyName
					        + "'. Failed to convert string '" + value
					        + "' to java.lang.Boolean. Cause: "
					        + e.getClass().getName() + ": " + e.getMessage());
				}
				result = defaultValue;
			}
		}
		return result;
	}

    /**
	 * Updates global property
	 *
	 * @param globalProperty Global property to be updated
	 * @throws BeanNotFoundException If no such global property exists in a
     * persistent storage
	 */
	public void updateProperty(GlobalProperty globalProperty) throws BeanNotFoundException {
		globalPropertyManager.updateProperty(globalProperty);
		dynamicProperties.put(globalProperty.getName(), globalProperty.getValue());
	}

	/**
	 * Retrieves global property with specified name
	 *
	 * @param name The name of global property to retrieve
	 * @return Global property with given name or <code>null</code> or nothing
     * was found
	 */
	public GlobalProperty retrieveGlobalProperty(String name) {
		return globalPropertyManager.retrieveGlobalProperty(name);
	}

	/**
	 * Reloads all properties from configuration file
	 */
	public void reloadProperties() {
		dynamicProperties = globalPropertyManager.loadDynamicProperties(true);
	}

}
